/*
 * Copyright 2010, 2011, 2012 mapsforge.org
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.mapsforge.android.maps.gml;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;

import net.opengis.gml.FeatureCollection;
import net.opengis.gml.featureMember;

import org.jibx.runtime.BindingDirectory;
import org.jibx.runtime.IBindingFactory;
import org.jibx.runtime.IUnmarshallingContext;
import org.jibx.runtime.JiBXException;
import org.mapsforge.android.maps.overlay.ArrayItemizedOverlay;
import org.mapsforge.android.maps.overlay.ArrayWayOverlay;
import org.mapsforge.android.maps.overlay.Overlay;
import org.mapsforge.android.maps.overlay.OverlayItem;
import org.mapsforge.android.maps.overlay.OverlayWay;
import org.mapsforge.core.model.GeoPoint;

import android.graphics.Paint;
import android.graphics.drawable.Drawable;

/**
 * @author art class that represents a JiBX kml binding. version 2.2 and it provides methods to parse kml files and to
 *         obtain Overlays from it.
 */
public class GMLSubsetBinding extends GMLBinding {

	private FeatureCollection cached = null;

	private ArrayItemizedOverlay itemizedOverlay;

	private static final String BINDING_PACKAGE = "net.opengis.gml";
	private static final String BINDING_NAME = "gml_binding";

	/**
	 * Creates a new KML Binding for schema net.opengis.kml.2.2 with the following Overlay style:
	 * 
	 * @param defaultItemMarker
	 *            the default Item Marker
	 * @param defaultPolyPaintFill
	 *            the Default Polygon Paint Fill
	 * @param defaultPolyPaintOutline
	 *            the Default Polygon Paint Outline
	 * @param defaultLinePaintFill
	 *            the default Line Paint fill
	 * @param defaultLinePaintOutline
	 *            the default line paint outline
	 */
	public GMLSubsetBinding(Drawable defaultItemMarker, Paint defaultPolyPaintFill, Paint defaultPolyPaintOutline,
			Paint defaultLinePaintFill, Paint defaultLinePaintOutline) {
		super();
		this.defaultItemMarker = defaultItemMarker;
		this.defaultPolyPaintFill = defaultPolyPaintFill;
		this.defaultPolyPaintOutline = defaultPolyPaintOutline;
		this.defaultLinePaintFill = defaultLinePaintFill;
		this.defaultLinePaintOutline = defaultLinePaintOutline;
	}

	@Override
	protected String getBindingName() {
		return BINDING_NAME;
	}

	@Override
	protected String getBindingPackage() {
		return BINDING_PACKAGE;
	}

	@Override
	public boolean isValidGML(File gmlFile) {
		FeatureCollection out = null;
		boolean res = false;

		try {
			out = checkCache(gmlFile);

			if (out != null) {
				// unmarshal OK... assuming all went smooth
				res = true;
			}
		} catch (FileNotFoundException e) {
			res = false;
			LOGGER.log(Level.FINER, "GMLSubsetDinding.isValid returning false due to FIleNotFoundException.");
			LOGGER.log(Level.FINEST, null, e);
		} catch (JiBXException e) {
			res = false;
			LOGGER.log(Level.FINER, "GMLSubsetDinding.isValid returning false due to JiBXException.");
			LOGGER.log(Level.FINEST, null, e);
		} catch (java.lang.ClassCastException e) {
			res = false;
			LOGGER.log(Level.FINER, "GMLSubsetDinding.isValid returning false due to ClassCastException.");
			LOGGER.log(Level.FINEST, null, e);
		}

		return res;
	}

	private FeatureCollection checkCache(File gmlFile) throws FileNotFoundException, JiBXException {
		FileReader fr;
		FeatureCollection out = null;

		if (this.cachedFile != null && this.cachedFile.equals(gmlFile) && this.cached != null)
			out = this.cached;
		else {
			this.cachedFile = gmlFile;

			fr = new FileReader(gmlFile);

			IBindingFactory jc = BindingDirectory.getFactory(BINDING_NAME, BINDING_PACKAGE);
			IUnmarshallingContext unmarshaller = jc.createUnmarshallingContext();
			out = (FeatureCollection) unmarshaller.unmarshalDocument(fr, BINDING_NAME);

			this.cached = out;
		}

		return out;
	}

	@Override
	public Collection<? extends Overlay> parseGML(File gmlFile) throws FileNotFoundException, JiBXException {
		List<Overlay> tmpList = Collections.synchronizedList(new ArrayList<Overlay>(INITIAL_CAPACITY));

		// Reading the File
		FeatureCollection out = checkCache(gmlFile);

		if (out == null) {
			JiBXException e = new org.jibx.runtime.JiBXException("Not Recognized Binding Package");
			throw e;
		}

		// For each feature in the document, determine it's type, and try to assign it to a suitable Overlay
		for (int index = 0; index < out.collection.size(); index++) {

			featureMember m = out.collection.get(index);

			// TODO: accept more features
			if (m.poly != null) {
				// TODO: treat the inner boundary too?
				if (m.poly.outerRing != null) {
					List<GeoPoint> cords = gmlStringCoordinatesToGeoPoints(m.poly.outerRing);

					ArrayWayOverlay wayOverlay = new ArrayWayOverlay(this.defaultPolyPaintFill,
							this.defaultPolyPaintOutline);

					GeoPoint[] temp = cords.toArray(new GeoPoint[cords.size()]);
					GeoPoint[][] cordList = new GeoPoint[][] { temp };

					if (m.poly.innerRing != null) {

						List<GeoPoint> innerCords = gmlStringCoordinatesToGeoPoints(m.poly.outerRing);

						GeoPoint[] temp2 = innerCords.toArray(new GeoPoint[innerCords.size()]);
						cordList = new GeoPoint[][] { temp, temp2 };
					}

					OverlayWay overlayWay = new OverlayWay(cordList, this.defaultPolyPaintFill,
							this.defaultPolyPaintOutline);

					wayOverlay.addWay(overlayWay);

					tmpList.add(wayOverlay);
				}
			} else {
				LOGGER.log(Level.FINER, "gml geometry feature not supported for element " + m.id + " " + m.name);
				// create an ItemizedOverlay with the default marker
				this.itemizedOverlay = new ArrayItemizedOverlay(this.defaultItemMarker);

				// create a GeoPoint with the latitude and longitude coordinates
				GeoPoint geoPoint = new GeoPoint(41.391361, 2.109350);

				// create an OverlayItem with title and description
				OverlayItem item = new OverlayItem(geoPoint, m.name, "" + m.id);

				// add the OverlayItem to the ArrayItemizedOverlay
				this.itemizedOverlay.addItem(item);
			}
		}

		// the points are special... and all gathered into the itemizedOverlay
		// we must add this special overlay here
		if (this.itemizedOverlay != null)
			tmpList.add(this.itemizedOverlay);

		return tmpList;
	}

	private static List<GeoPoint> gmlStringCoordinatesToGeoPoints(String c) {

		List<GeoPoint> cords = new ArrayList<GeoPoint>();

		String[] pairs = c.split(" ");
		for (int i = 0; i < pairs.length; i++) {
			String[] s = pairs[i].split(",");
			try {
				double lon = Double.parseDouble(s[0]);
				double lat = Double.parseDouble(s[1]);
				LOGGER.log(Level.INFO, "Using this coordinates: lat:" + lat + " lon: " + lon);
				GeoPoint geoPoint = new GeoPoint(lat, lon);
				cords.add(geoPoint);
			} catch (java.lang.ArrayIndexOutOfBoundsException e) {
				LOGGER.log(Level.WARNING, "gmlCoordinates not formatted as expected: " + c, e);
			}
		}

		return cords;
	}

	@Override
	public String composeGML(Collection<? extends Overlay> features) {
		// TODO Auto-generated method stub
		return null;
	}
}
